/*
 * Tezad - Classic puzzle game
 * Copyright 2009 Shayne Riley and Paul Maseberg
 *
 * Tezad is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tezad is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Tezad.  If not, see <http://www.gnu.org/licenses/>
 * or write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * To contact the authors on questions or issues regarding the license,
 * you may send an email to <pezads at gmail dot com>
 */

#ifndef GAMECORE_H_
#define GAMECORE_H_

#include <vector>
#include <queue>
#include "CommandActions.h"
#include "Command.h"
#include "Subject.h"
#include "GameWell.h"
#include "GameMinoStruct.h"
#include "GameMinoState.h"
#include "GameMinoEngine.h"

class GameCore : public CommandActions, public Subject
{
public:
    /// The constructor. Yes indeedy.
    GameCore();
    virtual ~GameCore();

    /**
     * Initializes the game, specifying the desired
     * width, height, hidden height, starting row and column of the origin
     * brick of the minos when they get introduced to the well, and starting
     * speed.
     * @param width The width of the well. Must be greater than or equal to 4.
     * @param totalHeight The height of the well. Must be greater or equal to 4.
     * @param hiddenHeight  The portion of the well that is hidden. Must be
     * greater or equal to 0, and cannot be equal to the total height of the
     * well.
     * @param startRow  The starting row of the origin brick of each new mino.
     *  Must be located within the confines of the well, and at least one row
     *  down from the top of the well, and two rows up from the bottom.
     *  Remember, row numbers go from 0 at the bottom to n - 1at the top, so you
     *  would want it at n - 2, most likely.
     * @param startCol  The starting column of the origin brick of each new
     * mino. Must be at least one column in from the left, and two columns in
     * from the right. Column numbers for from 0 at left to n - 1 at right.
     * @param startSpeed    The starting speed of the mino decent. Must be
     * between 0-20.
     */
    void initializeGame(int width = 10, int totalHeight = 22,
            int hiddenHeight = 2, int startRow = 20, int startCol = 4,
            int startSpeed = 0);

    /**
     * Advance the game progress by a number of steps.
     * @param timeStep The steps to increment the game by. Must be at least one.
     * It probably should also be less than some high number, like 2^15,
     * just because you'd be a jerk for doing that. But who is going to enforce
     * that, really.
     */
    void advanceTimeStep(long timeStep = 1);

    /**
     * Retrieve the current time step of the game. Not that it would matter to
     * anyone else.
     *
     * @return the current time step of the game. Should hopefully be 0 or
     * greater, but who knows, if the game goes on long enough it could
     * wrap around to negatives. Unless there is a bug somewhere, I'm betting
     * that it won't happen.
     */
    long currentTimeStep();

    /// The player (human or AI) wants to shift the active mino left.
    /// @param pressed  True: button was just pressed. False: just released.
    virtual void inputReqShiftLeft(bool pressed);

    /// The player (human or AI) wants to shift the active mino right.
    /// @param pressed  True: button was just pressed. False: just released.
    virtual void inputReqShiftRight(bool pressed);

    /// The player (human or AI) wants to shift the active mino down.
    /// @param pressed  True: button was just pressed. False: just released.
    virtual void inputReqSoftDown(bool pressed);

    /// The player (human or AI) wants to shift the active mino down to the
    /// lowest position in the well and set the mino.
    virtual void inputReqHardDown();

    /// The player (human or AI) wants to rotate the active mino
    /// counter-clockwise.
    virtual void inputReqRotateCCW();

    /// The player (human or AI) wants to rotate the active mino clockwise.
    virtual void inputReqRotateCW();

    /// The player (human or AI) wants to swap the active mino with the one in
    /// the hold.
    virtual void inputReqSwapActive();

    // notify() is implemented in Subject.

private:
    enum EnInputActs {left, right, soft, hard, ccw, cw, swap};
    typedef std::vector<Command*> CommandList;
    typedef std::queue<EnInputActs> InputQueue;

    GameWell well;
    int startingRow;
    int startingCol;
    int speed;
    long currentStep;
    int gravityStep;
    int settingStep;
    GameMinoStruct const *activeMino;
    GameMinoState minoState;
    GameMinoEngine minoEngine;
    InputQueue currentQueue;

    // Yes, these two are for input state and should really be put in another
    // class. I already made a note of it.
    bool heldButtons[3];
    int buttonStep[3];
    const static int REPEAT_DELAY = 16;
    const static int REPEAT_RATE = 2;

    bool performInputs(CommandList& commands); ///< @return true if hard drop.
    void performGravity(CommandList& commands, bool inHardDrop);
    bool performSets(CommandList& commands, bool inHardDrop); ///< @return true if set.
    void performClearing(CommandList& commands, bool inHasSet);
    void performNextMino(CommandList& commands, bool inHasSet);
    void announceCommands(CommandList& commands);
    void deleteCommands(CommandList& commands);
};

#endif /* GAMECORE_H_ */
